#pragma once

#include <stdint.h>
#include <stdlib.h>


namespace arduino_basic {

    /**
     * @brief
     *
     * @tparam LEN
     */
    template <size_t LEN>
    class WrapperToWriteCharBuffer {
        static_assert(LEN < 255);

       private:
        char _buf[1 + LEN] = {0};
        uint8_t _index = 0;

       public:
        uint8_t write(uint8_t ch) {
            _buf[_index] = ch;
            ++_index;
            return 1;
        }

        uint8_t write(const char *str) {
            uint8_t count = _index;

            while (*str) {
                write(*str);
                ++str;
            }

            return _index - count;
        }

        const char *buffer() const {
            return _buf;
        }
    };


    /**
     * @brief 与 WrapperToWriteCharBuffer 配合，用来取代utoa 和ultoa，同时支持实现了Print 接口的对象
     *
     * @tparam Writer
     * @param w
     * @param n
     * @param base
     * @return uint8_t
     */
    template <typename Writer>
    uint8_t write_unsigned_to_writer(Writer &w, uint32_t n, uint8_t base) {
        char buf[8 * sizeof(long) + 1];  // Assumes 8-bit chars plus zero byte.
        char *str = &buf[sizeof(buf) - 1];

        *str = '\0';

        // prevent crash if called with base == 1
        if (base < 2) base = 10;

        do {
            char c = n % base;
            n /= base;

            *--str = c < 10 ? c + '0' : c + 'A' - 10;
        } while (n);

        return w.write(str);
    }


    /**
     * @brief 与 WrapperToWriteCharBuffer 配合，用来取代itoa 和ltoa，同时支持实现了Print 接口的对象
     *
     * @tparam Writer
     * @param w
     * @param n
     * @param base
     * @return uint8_t
     */
    template <typename Writer>
    uint8_t write_signed_to_writer(Writer &w, int32_t n, uint8_t base) {
        if (n < 0) {
            w.write('-');
            n = 0 - n;
            return 1 + write_unsigned_to_writer(w, static_cast<uint32_t>(n), base);
        }
        return write_unsigned_to_writer(w, static_cast<uint32_t>(n), base);
    }


    /**
     * @brief 与 WrapperToWriteCharBuffer 配合，用来取代dtostrf，同时支持实现了Print 接口的对象
     *
     * @tparam Writer
     * @tparam FloatType
     * @param w
     * @param number
     * @param digits
     * @return size_t
     */
    template <typename Writer, typename FloatType>
    size_t write_float_to_writer(Writer &w, FloatType number, uint8_t digits) {
        size_t n = 0;

        if (isnan(number))
            return w.write("NAN");

        if (isinf(number))
            return w.write("INF");

        if (number > 4294967040.0)
            return w.write("OVF");  // constant determined empirically

        if (number < -4294967040.0)
            return w.write("OVF");  // constant determined empirically

        // Handle negative numbers
        if (number < 0.0) {
            n += w.write('-');
            number = -number;
        }

        // Round correctly so that print(1.999, 2) prints as "2.00"
        FloatType rounding = 0.5;

        switch (digits) {
            case 0:
                break;
            case 1:
                rounding = 0.05;
                break;
            case 2:
                rounding = 0.005;
                break;
            case 3:
                rounding = 0.0005;
                break;
            case 4:
                rounding = 0.00005;
                break;
            default:
                for (uint8_t i = 0; i < digits; ++i)
                    rounding /= 10.0;
        }

        number += rounding;

        // Extract the integer part of the number and print it
        unsigned long int_part = (unsigned long)number;
        FloatType remainder = number - (FloatType)int_part;
        n += write_unsigned_to_writer(w, int_part, 10);

        // Print the decimal point, but only if there are digits beyond
        if (digits > 0) {
            n += w.write('.');
        }

        // Extract digits from the remainder one at a time
        while (digits-- > 0) {
            remainder *= 10.0;
            unsigned int toPrint = (unsigned int)(remainder);
            n += write_unsigned_to_writer(w, toPrint, 10);
            remainder -= toPrint;
        }

        return n;
    }

}  // namespace arduino_basic